{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python Cheat Sheet\n",
    "\n",
    "Basic cheatsheet for Python mostly based on the book written by Al Sweigart, [Automate the Boring Stuff with Python](https://automatetheboringstuff.com/) under the [Creative Commons license](https://creativecommons.org/licenses/by-nc-sa/3.0/) and many other sources.\n",
    "\n",
    "## Read It\n",
    "\n",
    "- [Website](https://www.pythoncheatsheet.org)\n",
    "- [Github](https://github.com/wilfredinni/python-cheatsheet)\n",
    "- [PDF](https://github.com/wilfredinni/Python-cheatsheet/raw/master/python_cheat_sheet.pdf)\n",
    "- [Jupyter Notebook](https://mybinder.org/v2/gh/wilfredinni/python-cheatsheet/master?filepath=jupyter_notebooks)\n",
    "\n",
    "## Regular Expressions\n",
    "\n",
    "1. Import the regex module with `import re`.\n",
    "2. Create a Regex object with the `re.compile()` function. (Remember to use a raw string.)\n",
    "3. Pass the string you want to search into the Regex object’s `search()` method. This returns a `Match` object.\n",
    "4. Call the Match object’s `group()` method to return a string of the actual matched text.\n",
    "\n",
    "All the regex functions in Python are in the re module:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import re"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Matching Regex Objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phone_num_regex = re.compile(r'\\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d')\n",
    "mo = phone_num_regex.search('My number is 415-555-4242.')\n",
    "print('Phone number found: {}'.format(mo.group()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Grouping with Parentheses"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phone_num_regex = re.compile(r'(\\d\\d\\d)-(\\d\\d\\d-\\d\\d\\d\\d)')\n",
    "mo = phone_num_regex.search('My number is 415-555-4242.')\n",
    "mo.group(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo.group(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo.group(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo.group()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To retrieve all the groups at once: use the groups() method—note the plural form for the name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo.groups()\n",
    "area_code, main_number = mo.groups()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(area_code)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(main_number)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Matching Multiple Groups with the Pipe\n",
    "\n",
    "The | character is called a pipe. You can use it anywhere you want to match one of many expressions. For example, the regular expression r'Batman|Tina Fey' will match either 'Batman' or 'Tina Fey'."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hero_regex = re.compile (r'Batman|Tina Fey')\n",
    "mo1 = hero_regex.search('Batman and Tina Fey.')\n",
    "mo1.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo2 = hero_regex.search('Tina Fey and Batman.')\n",
    "mo2.group()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also use the pipe to match one of several patterns as part of your regex:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bat_regex = re.compile(r'Bat(man|mobile|copter|bat)')\n",
    "mo = bat_regex.search('Batmobile lost a wheel')\n",
    "mo.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo.group(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Optional Matching with the Question Mark\n",
    "\n",
    "The ? character flags the group that precedes it as an optional part of the pattern."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bat_regex = re.compile(r'Bat(wo)?man')\n",
    "mo1 = bat_regex.search('The Adventures of Batman')\n",
    "mo1.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo2 = bat_regex.search('The Adventures of Batwoman')\n",
    "mo2.group()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Matching Zero or More with the Star\n",
    "\n",
    "The * (called the star or asterisk) means “match zero or more”—the group that precedes the star can occur any number of times in the text."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bat_regex = re.compile(r'Bat(wo)*man')\n",
    "mo1 = bat_regex.search('The Adventures of Batman')\n",
    "mo1.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo2 = bat_regex.search('The Adventures of Batwoman')\n",
    "mo2.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo3 = bat_regex.search('The Adventures of Batwowowowoman')\n",
    "mo3.group()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Matching One or More with the Plus\n",
    "\n",
    "While * means “match zero or more,” the + (or plus) means “match one or more”. The group preceding a plus must appear at least once. It is not optional:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bat_regex = re.compile(r'Bat(wo)+man')\n",
    "mo1 = bat_regex.search('The Adventures of Batwoman')\n",
    "mo1.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo2 = bat_regex.search('The Adventures of Batwowowowoman')\n",
    "mo2.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo3 = bat_regex.search('The Adventures of Batman')\n",
    "mo3 is None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Matching Specific Repetitions with Curly Brackets\n",
    "\n",
    "If you have a group that you want to repeat a specific number of times, follow the group in your regex with a number in curly brackets. For example, the regex (Ha){3} will match the string 'HaHaHa', but it will not match 'HaHa', since the latter has only two repeats of the (Ha) group.\n",
    "\n",
    "Instead of one number, you can specify a range by writing a minimum, a comma, and a maximum in between the curly brackets. For example, the regex (Ha){3,5} will match 'HaHaHa', 'HaHaHaHa', and 'HaHaHaHaHa'."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ha_regex = re.compile(r'(Ha){3}')\n",
    "mo1 = ha_regex.search('HaHaHa')\n",
    "mo1.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo2 = ha_regex.search('Ha')\n",
    "mo2 is None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Greedy and Nongreedy Matching\n",
    "\n",
    "Python’s regular expressions are greedy by default, which means that in ambiguous situations they will match the longest string possible. The non-greedy version of the curly brackets, which matches the shortest string possible, has the closing curly bracket followed by a question mark."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "greedy_ha_regex = re.compile(r'(Ha){3,5}')\n",
    "mo1 = greedy_ha_regex.search('HaHaHaHaHa')\n",
    "mo1.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nongreedy_ha_regex = re.compile(r'(Ha){3,5}?')\n",
    "mo2 = nongreedy_ha_regex.search('HaHaHaHaHa')\n",
    "mo2.group()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The findall Method\n",
    "\n",
    "In addition to the search() method, Regex objects also have a findall() method. While search() will return a Match object of the first matched text in the searched string, the findall() method will return the strings of every match in the searched string."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phone_num_regex = re.compile(r'\\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d') # has no groups\n",
    "phone_num_regex.findall('Cell: 415-555-9999 Work: 212-555-0000')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To summarize what the findall() method returns, remember the following:\n",
    "\n",
    "- When called on a regex with no groups, such as \\d-\\d\\d\\d-\\d\\d\\d\\d, the method findall() returns a list of ng matches, such as ['415-555-9999', '212-555-0000'].\n",
    "\n",
    "- When called on a regex that has groups, such as (\\d\\d\\d)-d\\d)-(\\d\\ d\\d\\d), the method findall() returns a list of es of strings (one string for each group), such as [('415', ', '9999'), ('212', '555', '0000')].\n",
    "\n",
    "### Making Your Own Character Classes\n",
    "\n",
    "There are times when you want to match a set of characters but the shorthand character classes (\\d, \\w, \\s, and so on) are too broad. You can define your own character class using square brackets. For example, the character class [aeiouAEIOU] will match any vowel, both lowercase and uppercase."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "vowel_regex = re.compile(r'[aeiouAEIOU]')\n",
    "vowel_regex.findall('Robocop eats baby food. BABY FOOD.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also include ranges of letters or numbers by using a hyphen. For example, the character class [a-zA-Z0-9] will match all lowercase letters, uppercase letters, and numbers.\n",
    "\n",
    "By placing a caret character (^) just after the character class’s opening bracket, you can make a negative character class. A negative character class will match all the characters that are not in the character class. For example, enter the following into the interactive shell:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "consonant_regex = re.compile(r'[^aeiouAEIOU]')\n",
    "consonant_regex.findall('Robocop eats baby food. BABY FOOD.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The Caret and Dollar Sign Characters\n",
    "\n",
    "- You can also use the caret symbol (^) at the start of a regex to indicate that a match must occur at the beginning of the searched text.\n",
    "\n",
    "- Likewise, you can put a dollar sign ($) at the end of the regex to indicate the string must end with this regex pattern.\n",
    "\n",
    "- And you can use the ^ and $ together to indicate that the entire string must match the regex—that is, it’s not enough for a match to be made on some subset of the string.\n",
    "\n",
    "The r'^Hello' regular expression string matches strings that begin with 'Hello':"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "begins_with_hello = re.compile(r'^Hello')\n",
    "begins_with_hello.search('Hello world!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "begins_with_hello.search('He said hello.') is None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The r'\\d$' regular expression string matches strings that end with a numeric character from 0 to 9:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "whole_string_is_num = re.compile(r'^\\d+$')\n",
    "whole_string_is_num.search('1234567890')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "whole_string_is_num.search('12345xyz67890') is None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "whole_string_is_num.search('12 34567890') is None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The Wildcard Character\n",
    "\n",
    "The . (or dot) character in a regular expression is called a wildcard and will match any character except for a newline:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "at_regex = re.compile(r'.at')\n",
    "at_regex.findall('The cat in the hat sat on the flat mat.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Matching Everything with Dot-Star"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "name_regex = re.compile(r'First Name: (.*) Last Name: (.*)')\n",
    "mo = name_regex.search('First Name: Some Last Name: One')\n",
    "mo.group(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mo.group(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The dot-star uses greedy mode: It will always try to match as much text as possible. To match any and all text in a nongreedy fashion, use the dot, star, and question mark (.*?). The question mark tells Python to match in a nongreedy way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nongreedy_regex = re.compile(r'<.*?>')\n",
    "mo = nongreedy_regex.search('<To serve man> for dinner.>')\n",
    "mo.group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "greedy_regex = re.compile(r'<.*>')\n",
    "mo = greedy_regex.search('<To serve man> for dinner.>')\n",
    "mo.group()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Matching Newlines with the Dot Character\n",
    "\n",
    "The dot-star will match everything except a newline. By passing re.DOTALL as the second argument to re.compile(), you can make the dot character match all characters, including the newline character:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "no_newline_regex = re.compile('.*')\n",
    "no_newline_regex.search('Serve the public trust.\\nProtect the innocent.\\nUphold the law.').group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "newline_regex = re.compile('.*', re.DOTALL)\n",
    "newline_regex.search('Serve the public trust.\\nProtect the innocent.\\nUphold the law.').group()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Review of Regex Symbols\n",
    "\n",
    "| Symbol                   | Matches                                                      |\n",
    "| ------------------------ | ------------------------------------------------------------ |\n",
    "| `?`                      | zero or one of the preceding group.                          |\n",
    "| `*`                      | zero or more of the preceding group.                         |\n",
    "| `+`                      | one or more of the preceding group.                          |\n",
    "| `{n}`                    | exactly n of the preceding group.                            |\n",
    "| `{n,}`                   | n or more of the preceding group.                            |\n",
    "| `{,m}`                   | 0 to m of the preceding group.                               |\n",
    "| `{n,m}`                  | at least n and at most m of the preceding p.                 |\n",
    "| `{n,m}?` or `*?` or `+?` | performs a nongreedy match of the preceding p.               |\n",
    "| `^spam`                  | means the string must begin with spam.                       |\n",
    "| `spam$`                  | means the string must end with spam.                         |\n",
    "| `.`                      | any character, except newline characters.                    |\n",
    "| `\\d`, `\\w`, and `\\s`     | a digit, word, or space character, resectively.              |\n",
    "| `\\D`, `\\W`, and `\\S`     | anything except a digit, word, or space acter, respectively. |\n",
    "| `[abc]`                  | any character between the brackets (such as a, b, ).         |\n",
    "| `[^abc]`                 | any character that isn’t between the brackets.              |\n",
    "\n",
    "### Case-Insensitive Matching\n",
    "\n",
    "To make your regex case-insensitive, you can pass re.IGNORECASE or re.I as a second argument to re.compile():"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "robocop = re.compile(r'robocop', re.I)\n",
    "robocop.search('Robocop is part man, part machine, all cop.').group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "robocop.search('ROBOCOP protects the innocent.').group()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "robocop.search('Al, why does your programming book talk about robocop so much?').group()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Substituting Strings with the sub() Method\n",
    "\n",
    "The sub() method for Regex objects is passed two arguments:\n",
    "\n",
    "1. The first argument is a string to replace any matches.\n",
    "1. The second is the string for the regular expression.\n",
    "\n",
    "The sub() method returns a string with the substitutions applied:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "names_regex = re.compile(r'Agent \\w+')\n",
    "names_regex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_names_regex = re.compile(r'Agent (\\w)\\w*')\n",
    "agent_names_regex.sub(r'\\1****', 'Agent Alice told Agent Carol that Agent Eve knew Agent Bob was a double agent.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Managing Complex Regexes\n",
    "\n",
    "To tell the re.compile() function to ignore whitespace and comments inside the regular expression string, “verbose mode” can be enabled by passing the variable re.VERBOSE as the second argument to re.compile().\n",
    "\n",
    "Now instead of a hard-to-read regular expression like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phone_regex = re.compile(r'((\\d{3}|\\(\\d{3}\\))?(\\s|-|\\.)?\\d{3}(\\s|-|\\.)\\d{4}(\\s*(ext|x|ext.)\\s*\\d{2,5})?)')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "you can spread the regular expression over multiple lines with comments like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phone_regex = re.compile(r'''(\n",
    "    (\\d{3}|\\(\\d{3}\\))?            # area code\n",
    "    (\\s|-|\\.)?                    # separator\n",
    "    \\d{3}                         # first 3 digits\n",
    "    (\\s|-|\\.)                     # separator\n",
    "    \\d{4}                         # last 4 digits\n",
    "    (\\s*(ext|x|ext.)\\s*\\d{2,5})?  # extension\n",
    "    )''', re.VERBOSE)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
